/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Xml;
using System.Xml.XPath;

namespace DaveSexton.DocProject.Sandcastle.BuildComponents
{
	public sealed class SandcastleBuildComponentCollection : MarshalByRefCollection<SandcastleBuildComponent>, IBindingList, ISupportInitialize
	{
		#region Public Properties
		public bool HasChanges
		{
			get
			{
				return (initializing || suspended) ? lastChangesValue : hasChanges;
			}
		}
		#endregion

		#region Private / Protected
		private bool initializing, hasChanges, suspended, lastChangesValue;
		private readonly SandcastleBuildComponentManager owner;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="SandcastleBuildComponentCollection" /> class.
		/// </summary>
		internal SandcastleBuildComponentCollection(SandcastleBuildComponentManager owner)
		{
			this.owner = owner;
		}
		#endregion

		#region Methods
		protected override void ClearItems()
		{
			int count = Count;

			foreach (SandcastleBuildComponent component in this)
				component.PropertyChanged -= component_PropertyChanged;

			base.ClearItems();

			if (count > 0)
			{
				hasChanges = true;
				OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
			}
		}

		protected override void RemoveItem(int index)
		{
			SandcastleBuildComponent component = this[index];

			component.PropertyChanged -= component_PropertyChanged;

			base.RemoveItem(index);

			hasChanges = true;
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
		}

		protected override void InsertItem(int index, SandcastleBuildComponent item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			if (item.Manager != owner)
				throw new ArgumentException(Resources.Errors.ComponentCollectionDifferentManager, "item");

			item.PropertyChanged += component_PropertyChanged;

			base.InsertItem(index, item);

			hasChanges = true;
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
		}

		protected override void SetItem(int index, SandcastleBuildComponent item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			if (item.Manager != owner)
				throw new ArgumentException(Resources.Errors.ComponentCollectionDifferentManager, "item");

			SandcastleBuildComponent component = this[index];

			component.PropertyChanged -= component_PropertyChanged;
			item.PropertyChanged += component_PropertyChanged;

			base.SetItem(index, item);

			hasChanges = true;
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
		}

		internal void MoveUp(SandcastleBuildComponent item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			int index = IndexOf(item);
			int newIndex = index - 1;

			RemoveAt(index);
			Insert(newIndex, item);

			hasChanges = true;
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, index));
		}

		internal void MoveDown(SandcastleBuildComponent item)
		{
			if (item == null)
				throw new ArgumentNullException("item");

			int index = IndexOf(item);
			int newIndex = index + 1;

			RemoveAt(index);
			Insert(newIndex, item);

			hasChanges = true;
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, index));
		}

		public void SuspendChangeTacking()
		{
			if (!suspended)
			{
				suspended = true;
				lastChangesValue = hasChanges;
				hasChanges = false;
			}
		}

		public void ResumeChangeTracking()
		{
			ResumeChangeTracking(true);
		}

		public void ResumeChangeTracking(bool raiseEventIfChanged)
		{
			if (suspended)
			{
				suspended = false;
				bool changed = hasChanges;
				hasChanges = lastChangesValue;

				if (raiseEventIfChanged && changed)
					OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
			}
		}
		#endregion

		#region Events
		private readonly object ListChangedEventLock = new object();
		private ListChangedEventHandler ListChangedEvent;

		/// <summary>
		/// Event raised when the list changes.
		/// </summary>
		public event ListChangedEventHandler ListChanged
		{
			add
			{
				lock (ListChangedEventLock)
				{
					ListChangedEvent += value;
				}
			}
			remove
			{
				lock (ListChangedEventLock)
				{
					ListChangedEvent -= value;
				}
			}
		}
		
		/// <summary>
		/// Raises the <see cref="ListChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="ListChangedEventArgs" /> object that provides the arguments for the event.</param>
		private void OnListChanged(ListChangedEventArgs e)
		{
			if (initializing || suspended)
				return;

			ListChangedEventHandler handler = null;

			lock (ListChangedEventLock)
			{
				handler = ListChangedEvent;
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion

		#region Event Handlers
		private void component_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			hasChanges = true;

			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOf((SandcastleBuildComponent) sender)));
		}
		#endregion

		#region ISupportInitialize Members
		public void BeginInit()
		{
			if (!initializing)
			{
				initializing = true;
				lastChangesValue = hasChanges;
			}
		}

		public void EndInit()
		{
			if (initializing)
			{
				initializing = false;
				hasChanges = lastChangesValue;
			}
		}
		#endregion

		#region IBindingList Members
		void IBindingList.AddIndex(PropertyDescriptor property)
		{
			// do nothing
		}

		object IBindingList.AddNew()
		{
			throw new NotSupportedException();
		}

		bool IBindingList.AllowEdit
		{
			get
			{
				return true;
			}
		}

		bool IBindingList.AllowNew
		{
			get
			{
				return false;
			}
		}

		bool IBindingList.AllowRemove
		{
			get
			{
				return true;
			}
		}

		void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction)
		{
			throw new NotSupportedException();
		}

		int IBindingList.Find(PropertyDescriptor property, object key)
		{
			throw new NotSupportedException();
		}

		bool IBindingList.IsSorted
		{
			get
			{
				return true;
			}
		}

		void IBindingList.RemoveIndex(PropertyDescriptor property)
		{
			// do nothing
		}

		void IBindingList.RemoveSort()
		{
			throw new NotSupportedException();
		}

		ListSortDirection IBindingList.SortDirection
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		PropertyDescriptor IBindingList.SortProperty
		{
			get
			{
				throw new NotSupportedException();
			}
		}

		bool IBindingList.SupportsChangeNotification
		{
			get
			{
				return true;
			}
		}

		bool IBindingList.SupportsSearching
		{
			get
			{
				return false;
			}
		}

		bool IBindingList.SupportsSorting
		{
			get
			{
				return false;
			}
		}
		#endregion
	}
}
